Fork me on GitHub
余鸢

Exporting和Consuming模块

创建一个hello-world.js模块

hello-world.js

1
2
3
4
// Node提供了`module.exports`接口来将函数和变量公开给其他文件
module.exports = function(subject) {
console.log('Hello ' + subject);
};

hello-mars.js

1
2
3
4
5
// 如果我们不希望整个导出为单个对象,我们可以将函数和变量导出为`exports`对象的属性
// 我们省略了“modules”。 从export 当我们这样使用它
exports.hello = function(subject) {
console.log('Mars says Hello ' + subject);
};

hello-venus.js

1
2
3
4
5
6
7
8
// 另一种使用modules.export的方法
function hello(subject) {
console.log('Venus says Hello ' + subject);
}
module.exports = {
hello: hello
};

hello-jupiter.js

1
2
3
4
5
6
7
8
9
10
// 使用modules.export的另一种方法是通过定义导出中的函数
module.exports = {
hello: function(subject) {
console.log('Jupiter says hello ' + subject);
},
bye: function(subject) {
console.log('Jupiter says goodbye ' + subject);
}
};

加载模块与目录名称

我们有一个名为hello的目录,其中包含以下文件:

index.js

1
2
3
4
// hello/index.js
module.exports = function(){
console.log('Hej');
};

main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// hello/main.js
// 我们可以通过使用`require()`方法来包括我们定义的其他文件
var hw = require('./hello-world.js'),
hm = require('./hello-mars.js'),
hv = require('./hello-venus.js'),
hj = require('./hello-jupiter.js'),
hu = require('./index.js');
// 因为我们将功能分配给整个`module.exports`对象,我们可以直接使用它
hw('World!'); // outputs "Hello World!"
// 在这种情况下,我们将功能分配给出口的“hello”属性,所以我们也必须在这里使用
hm.hello('Solar System!'); // outputs "Mars says Hello Solar System!"
// 一次分配module.exports的结果与hello-world.js中的相同
hv.hello('Milky Way!'); // outputs "Venus says Hello Milky Way!"
hj.hello('Universe!'); // outputs "Jupiter says hello Universe!"
hj.bye('Universe!'); // outputs "Jupiter says goodbye Universe!"
hu(); //output 'hej'

加载和使用module

module可以通过require()函数“imported”,否则“required”。 例如,要加载Node.js附带的http模块,可以使用以下内容:

1
var http = require('http');

除运行时附带的模块外,您还可以要求从npm安装的模块,如express。 如果您已经通过npm install express在系统上安装了express,可以简单地写:

1
var express = require('express');

您还可以将自己编写的模块作为应用程序的一部分。 在这种情况下,要在与当前文件相同的目录中包含一个名为lib.js的文件:

1
var mylib = require('./lib');

请注意,您可以省略扩展名,并假定.js。 加载模块后,该变量将填充一个包含从所需文件发布的方法和属性的对象。 一个完整的例子:

1
2
3
4
5
6
7
8
9
10
11
var http = require('http');
// `http`模块具有“STATUS_CODES”属性
console.log(http.STATUS_CODES[404]); // outputs 'Not Found'
// 还包含`createServer()`
http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('<html><body>Module Test</body></html>');
res.end();
}).listen(80);

每个模块只注入一次

NodeJS只在您首次需要时执行该模块。 任何进一步的需求函数都将执行相同的对象。 Node也首先使用require加载模块。 这减少了文件读取的数量,并有助于加快应用程序的速度。

myModule.js

1
2
console.log(123)
exports.myFunction='great'

index.js

1
2
3
4
5
6
var a=require('./myModule') // Output 123
var b=require('./myModule') // No output
console.log(a) // Output 'great'
console.log(b) // Output 'great'
a.a=5
console.log(b.a) // Output 5

文件夹作为模块

模块可以分割在同一文件夹中的许多.js文件中。 my_module文件夹中的示例:

function_one.js

1
2
3
module.exports = function() {
return 1;
}

function_two.js

1
2
3
module.exports = function() {
return 2;
}

index.js

1
2
exports.f_one = require('./function_one.js');
exports.f_two = require('./function_two.js');

通过文件夹名称引用一个类似这样的模块:

1
var split_module = require('./my_module');

请注意,如果您通过从require函数参数中省略./或任何指向文件夹的路径的要求,Node将尝试从node_modules文件夹加载模块。

或者,您可以在同一个文件夹中创建一个包含以下内容的package.json文件:

1
2
3
4
{
"name": "my_module",
"main": "./your_main_entry_point.js"
}

这样你就不需要命名主模块文件“index”。

建立自己的模块

您还可以引用一个对象来公开导出并持续将方法附加到该对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var auth = module.exports = {};
var config = require('../config');
var request = require('request');
auth.email = function (data, callback) {
// Authenticate with an email address
};
auth.facebook = function (data, callback) {
// Authenticate with a Facebook account
};
auth.twitter = function (data, callback) {
// Authenticate with a Twitter account
};
auth.slack = function (data, callback) {
// Authenticate with a Slack account
};
auth.stack_overflow = function (data, callback) {
// Authenticate with a Stack Overflow account
};

要使用其中的任何一个,只要按照通常的方式要求模块:

1
2
3
4
5
6
7
8
9
10
var auth = require('./auth');
module.exports = function (req, res, next) {
auth.facebook(req.body, function (err, user) {
if (err) return next(err);
req.user = user;
next();
});
};

使模块缓存无效

在开发中,您可能会发现在同一模块上多次使用require()会始终返回相同的模块,即使您对该文件进行了更改。 这是因为模块在第一次加载时被缓存,并且任何后续的模块加载将从缓存加载。

要解决这个问题,您必须删除缓存中的条目。 例如,如果您加载了一个模块:

1
var a = require('./a');

然后,您可以删除缓存条目:

1
2
var rpath = require.resolve('./a.js');
delete require.cache[rpath];

然后再次要求模块:

1
var a = require('./a');

请注意,这不是建议在生产中,因为delete只会删除对加载的模块的引用,而不是加载的数据本身。 该模块不是垃圾回收,因此不正确使用此功能可能会导致内存泄漏。

从node_modules加载模块

可以通过require 将d模块放在一个名为node_modules的特殊目录中而不需要使用相关路径。

例如,要从文件index.jsrequire一个名为foo的模块,可以使用以下目录结构:

1
2
3
4
5
index.js
\- node_modules
\- foo
|- foo.js
\- package.json

模块应该放在一个目录下,以及一个package.json文件。 package.json文件的main字段应该指向您的模块的入口点 - 这是用户导入的文件require('your-module')。 如果没有提供,main默认为index.js。 或者,您可以通过将相对路径附加到require调用:require('your-module/path/to/file')来引用相对于模块的文件。

Modules 也能从node_modules require d 。 如果我们有以下目录结构:

1
2
3
4
5
6
7
my-project
\- node_modules
|- foo // the foo module
\- ...
\- baz // the baz module
\- node_modules
\- bar // the bar module

我们将能够使用require('foo')来要求来自bar内任何文件的模块foo

请注意,节点将只匹配文件系统层级中最接近文件的模块,从(文件的当前目录/ node_modules)开始。 节点通过这种方式将目录匹配到文件系统根目录。

您可以从npm注册表或其他npm注册表安装新模块,或者自行创建。

虽然Node.js中的所有内容通常都是异步执行的,但require()不是其中之一。 由于模块在实践中只需要加载一次,因此它是一个阻塞操作,应该被正确使用。

模块在第一次加载之后被缓存。 如果您正在开发中编辑模块,则需要在模块缓存中删除其条目才能使用新的更改。 也就是说,即使模块被清除出模块缓存,模块本身也不是垃圾回收的,所以在生产环境中应该小心使用它。